#!/usr/bin/env python
# coding=utf-8
# CopyRight(c) 2019 Inspur Electronic Information Industry Co.,Ltd

"""
    secchecksuite/utils.py
    utils.py for secchecksuite include some common utils
    :CopyRight(c) 2019 Inspur Electronic Information Industry Co.,Ltd
"""

import io
import json
import os
import signal
import subprocess
import sys
import tempfile
from threading import Timer

import constant as const
import pluginfactory
import python_version_factory
import scenefactory
import seccheck_log


def load_config_file(config_full_file):
    """
    load main configure info
    Args:
        config_full_file: main framework full configure name
    Returns:
        json string about configure info
    Raises:
        json.decoder.JSONDecodeError:
        IOError: main config file not exist
    """

    if os.path.exists(config_full_file):
        try:
            with io.open(config_full_file, 'r', encoding='utf-8') as f:
                config_json = json.load(f)
                return config_json
        except BaseException as e:
            raise e
    else:
        raise IOError('{} config file not exist'.format(config_full_file))


def operation_environment(plugin):
    """check plugin_exe's operation environment
    Args:
        plugin: plugin infos include plugin_exe and his hasvalue stored in database
    Returns:
        scenetype: local/remote
    Raises:
        BaseException:  An error occurred cleanup a table
    """

    if 'local' == plugin.get('scene', None):
        return scenefactory.LocalExePluginScene()
    elif 'remote' == plugin.get('scene', None):
        return scenefactory.RemoteExePluginScene()
    else:
        return None


def get_plugin_type(plugin_type):
    """get plugin_exe'plugin_type,script or exe
    Args:
        plugin_type:
    Returns:
        plugin_type: script or exe
    Raises:
        BaseException:  An error occurred cleanup a table
    """

    if 'script' == plugin_type:
        return pluginfactory.ScriptPluginType()
    elif 'exe' == plugin_type:
        return pluginfactory.ExePluginType()
    else:
        return None


def joint_error_info(plugin_info, err):
    """
    joint error info when execute plugins
    Args:
        plugin_info: the plugin that executing
        err: the error when execute plugin
    Returns:

    """
    des = 'when exec plugin {} accur unknown error: {}'.format(plugin_info['plugin_exe'], err)
    info = {"code": 1, "count": 0, "des": des}
    seccheck_log.sec_check_logger.error(info['des'])
    return info


def judge_python():
    """
    judge python version
    Returns:
        object of python version
    """
    if sys.version_info > (3, 0):
        return python_version_factory.PythonVersion3()
    elif sys.version_info > (2, 0):
        return python_version_factory.PythonVersion2()
    else:
        raise BaseException('you should install python3.x or python2.x')


def reverse_list(input_list):
    """
    reverse the list
    Args:
        input_list: the input_list to be reverse
    Returns:
        list: input_list after to be reverse
    """
    if not isinstance(input_list, list):
        seccheck_log.sec_check_logger.info(
            'reverse_list fail.the input param type is not list but{}'.format(type(input_list)))
        return input_list

    reverse_list = []
    for i in range(0, len(input_list)):
        reverse_list.insert(0, input_list[i])

    seccheck_log.sec_check_logger.info('reverse_list success.')
    return reverse_list


def program_exit(file, status=0):
    """
    program exit
    Args:
        file: the file to be remove
        status: exit status
    Returns:
        None
    """
    basename = os.path.splitext(os.path.abspath(file))[0].replace(
        "/", "").replace(":", "").replace("\\", "-") + '-%s' % 'security' + '.lock'
    remove_file = os.path.normpath(tempfile.gettempdir() + '/' + basename)
    try:
        if os.path.exists(remove_file):
            os.remove(remove_file)
    except BaseException as e:
        print('remove file {} failed'.format(remove_file))
    finally:
        sys.exit(status)


class TimeOut:
    """
    TimeOut class: handler timeout
    Attributes:
        is_timeout: judge the process is really timeout
        time_out: timeout should be set
        command: the command should be execute
        is_start: judge the function that callable is start or get_plugin_info
    """

    def __init__(self, command, time_out, plugin_info, is_start=True):
        self.is_timeout = False
        self.time_out = time_out
        self.command = command
        self.plugin_info = plugin_info
        self.is_start = is_start

    def timeout_callback(self, p):
        """
        kill the process while timeout
        Args:
            p: the process that running
        Returns:
            None
        """
        self.is_timeout = True
        try:
            os.kill(p.pid, signal.SIGKILL)
        except Exception as err:
            seccheck_log.sec_check_logger.error('command run timeout: {}'.format(err))

    def run_command_with_time_out(self):
        """
        run command with timeout
        Returns:
            out_dicts: the result subprocess run
        """

        result = subprocess.Popen(self.command, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                                  shell=True)
        timer = Timer(self.time_out, self.timeout_callback, [result])
        timer.start()
        try:
            outs = filter(None, result.stdout.readlines())
            outs_dict = dict()
            for item in outs:
                item_dict = eval(item)
                for key in item_dict.keys():
                    key_lower = key.lower()
                    if self.is_start and key_lower in const.CODE_COUNT_DES:
                        outs_dict[key] = item_dict[key]
                    if not self.is_start and key_lower == 'plugin_info':
                        outs_dict = item_dict[key]
                        break
            if not outs_dict and self.is_start:
                return joint_error_info(self.plugin_info, 'result is none')
            return outs_dict
        except BaseException as err:
            outs_dict = []
            if self.is_start:
                outs_dict = joint_error_info(self.plugin_info, err)
            return outs_dict
        finally:
            timer.cancel()
            self.is_timeout = False
